#ifndef SPREADSHEET_H
#define SPREADSHEET_H

#include <QTableWidget>
#include "Cell.h"

template <class Type>class CMatrix;
class CCell;
class CSpreadsheetCompare;
class CMainWindow;
using namespace std;

class CSpreadsheet : public QTableWidget
{
    Q_OBJECT

public:
    CSpreadsheet(QWidget *pParent_ = 0);

    bool                       autoRecalculate() const { return autoRecalc; }
    QString                    currentLocation() const;
    QString                    currentFormula()  const;
    QTableWidgetSelectionRange selectedRange()   const;
    void                       clear();
    bool                       readFile (const QString &fileName);
    bool                       writeFile(const QString &fileName);
    void                       sort     (const CSpreadsheetCompare &compare);
    const QString&             getCurrentFile()  const {return _sCurFile;}
    void                       setCurrentFile(const QString& sPathFileNameExt_);
    static CSpreadsheet*       open(QWidget *pParent_);
    static CSpreadsheet*       openFile(const QString& sFileName_, QWidget *pParent_, QStringList* plFileNames_=NULL);
    void                       createFile(const QString& sPathFileNameExt_);
    bool                       save();
    bool                       saveAs();
    void                       initPrjContent(const QStringList& lFileNames_);
    QString                    fileType() const;
    const QString&             currentFile() const {return _sCurFile;}
    void                       search(const QString& sKey_,int nNum_, string* pResult_ );
    void                       search(const QString& sKey_,int nNum_, vector<string>* pvsResult_ );
    template <class T> void    search(const QString& sKey_,int nDim_, vector<T>* pResult_ );
    template <class T> void    search(const QString& sKey_,int nNum_, CMatrix<T>* pResult_ );
    template <class T> void    search(const QString& sKey_, T* pResult_);
    void searchMatrix( const QString& sKey_,CMatrix<double>* pMat_ );
    void searchColumnString( const QString& sKey_,vector<std::string>* pMat_ );
    void searchColumnInt( const QString& sKey_,vector<int>* pMat_ );

    bool addColumnVector(int nCol_,QString& sName_, const CMatrix<double>& mM_ );
    bool addColumnVector(int nCol_,QString& sName_, const vector<double>& vM_ );
    bool addMatrix(int nRow_,int nCol_,QString& sName_, const CMatrix<double>& mM_ );
    bool findNext(const QString &str, Qt::CaseSensitivity cs);
    bool findPrevious(const QString &str,Qt::CaseSensitivity cs);
    void cut();
    void cutCurrentRow(); // cut rows and move the following rows upward
    void removeCurrentRow(); // delete selected rows and move the following rows upward
    void insertRowAboveCurrent(); // insert an empty row above current row
    void copy();
    void paste();
    void del();
    void selectCurrentRow();
    void selectCurrentColumn();
    void sortBySelectedColumn();
    
public slots:
    void recalculate();
    void setAutoRecalculate(bool recalc);
    void closeEvent(QCloseEvent *event);
    void setSubWindowIcons(const QString& sPathFileNameExt_);

signals:
    void modified();
private slots:
    void somethingChanged();
    void updateStatusBar();

private:
    enum { MagicNumber = 0x7F51C883, RowCount = 100, ColumnCount = 10 };

    CCell *cell(int row, int column) const;
    QString text(int row, int column) const;
    QString formula(int row, int column) const;
    void setFormula(int row, int column, const QString &formula);
    bool saveFile(const QString &sFileName_);
    bool okToContinue();
    void switchRows(int nRow1_, int nRow2_);
    bool autoRecalc;
    bool _bIsUntitled;
    QString  _sCurFile;
    QAction* _pAct;
    Qt::SortOrder _eSortOrder;
};

class CSpreadsheetCompare
{
public:
    bool operator()(const QStringList &row1,
                    const QStringList &row2) const;

    enum { KeyCount = 1 };
    int keys[KeyCount];
    bool ascending[KeyCount];
};


template <class T>
void CSpreadsheet::search( const QString& sKey_, T* pResult_ )
{
    Q_ASSERT(pResult_);
    for(int r=0;r<RowCount;r++)
        for(int c=0;c<ColumnCount;c++){
            if(cell(r,c)&&cell(r,c)->text().contains(sKey_)){
                if(!cell(r,c+1)) 
                {
                    QString sError("the entry after \"");
                    sError += sKey_;
                    sError += "\" should not be empty.\n";
                    sError += currentFile();
                    sError += ".\n";
                    throw exception(sError.toStdString().c_str());
                }
                helpers::validate(*pResult_,cell(r,c+1)->formula().toStdString());
                //(*pResult_)=(float)cell(r,c+1)->formula().toDouble();
                return;
            }  
        }

        {
            QString sError("you need to specify \"");
            sError += sKey_;
            sError += "\".\n";
            sError += currentFile();
            sError += ".\n";
            throw exception(sError.toStdString().c_str());
        }
        return;
}

template <class T>
void CSpreadsheet::search( const QString& sKey_,int nDim_, vector<T>* pResult_ )
{
    Q_ASSERT(pResult_);
    pResult_->clear();
    pResult_->resize(nDim_,T(0));
    for(int r=0;r<RowCount;r++)
        for(int c=0;c<ColumnCount;c++){
            if(cell(r,c)&&cell(r,c)->text().contains(sKey_)){
                for(int i =0; i<nDim_;i++){
                    if(!cell(r,c+1+i)) 
                    {
                        QString sError("the entry after \"");
                        sError += sKey_;
                        sError += "\" should not be empty.\n";
                        sError += currentFile();
                        sError += ".\n";
                        throw exception(sError.toStdString().c_str());
                    }
                    //pResult_->push_back(cell(r,c+1+i)->formula().toDouble());
                    helpers::validate((*pResult_)[i],cell(r,c+1+i)->formula().toStdString());
                }
                return;
            }  
        }

        {
            QString sError("you need to specify \"");
            sError += sKey_;
            sError += "\".\n";
            sError += currentFile();
            sError += ".\n";
            throw exception(sError.toStdString().c_str());
        }
        return;
}

template <class T>
void CSpreadsheet::search( const QString& sKey_,int nNum_, CMatrix<T>* pResult_ )
{
    Q_ASSERT(pResult_);
    pResult_->SetSize(nNum_);
    for(int r=0;r<RowCount;r++)
        for(int c=0;c<ColumnCount;c++){
            if(cell(r,c)&&cell(r,c)->text().contains(sKey_)){
                for(int i =0; i<nNum_;i++){
                    if(!cell(r,c+1+i)) 
                    {
                        QString sError("the entry after \"");
                        sError += sKey_;
                        sError += "\" should not be empty.\n";
                        sError += currentFile();
                        sError += ".\n";
                        throw exception(sError.toStdString().c_str());
                    }
                    //(*pResult_)(i)=cell(r,c+1+i)->formula().toDouble();
                    helpers::validate((*pResult_)(i),cell(r,c+1+i)->formula().toStdString());
                }
                return;
            }  
        }

        {
            QString sError("you need to specify \"");
            sError += sKey_;
            sError += "\".\n";
            sError += currentFile();
            sError += ".\n";
            throw exception(sError.toStdString().c_str());
        }
        return;
}



#endif // SPREADSHEET_H